﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sensor.Common.Buffer;

/// <summary>
/// 线程安全的并发环形缓冲区
/// 适用于生产者-消费者模式，特别适合传感器数据流处理场景
/// </summary>
/// <typeparam name="T">缓冲区中存储的数据类型</typeparam>
public class ConcurrentCircularBuffer<T>
{
    #region 私有字段

    /// <summary>
    /// 存储数据的内部数组
    /// </summary>
    private readonly T[] _buffer;
    
    /// <summary>
    /// 指向下一个写入位置的索引（头指针）
    /// </summary>
    private int _head;
    
    /// <summary>
    /// 指向下一个读取位置的索引（尾指针）
    /// </summary>
    private int _tail;
    
    /// <summary>
    /// 当前缓冲区中的元素数量
    /// </summary>
    private int _count;
    
    /// <summary>
    /// 缓冲区的总容量
    /// </summary>
    private readonly int _capacity;
    
    /// <summary>
    /// 用于同步访问的锁对象
    /// </summary>
    private readonly object _lockObj = new();

    #endregion

    #region 公共属性

    /// <summary>
    /// 获取当前缓冲区中的元素数量
    /// </summary>
    public int Count => _count;
    
    /// <summary>
    /// 获取缓冲区的总容量
    /// </summary>
    public int Capacity => _capacity;
    
    /// <summary>
    /// 获取缓冲区是否为空
    /// </summary>
    public bool IsEmpty => _count == 0;
    
    /// <summary>
    /// 获取缓冲区是否已满
    /// </summary>
    public bool IsFull => _count == _capacity;

    #endregion

    #region 构造函数

    /// <summary>
    /// 初始化一个新的并发环形缓冲区实例
    /// </summary>
    /// <param name="capacity">缓冲区的容量，必须大于0</param>
    /// <exception cref="ArgumentException">当容量小于或等于0时抛出</exception>
    public ConcurrentCircularBuffer(int capacity)
    {
        if (capacity <= 0) throw new ArgumentException("容量必须大于0", nameof(capacity));
        _capacity = capacity;
        _buffer = new T[capacity];
    }

    #endregion

    #region 公共方法

    /// <summary>
    /// 尝试向缓冲区添加单个元素
    /// </summary>
    /// <param name="item">要添加的元素</param>
    /// <returns>如果成功添加返回true，如果缓冲区已满返回false</returns>
    public bool TryEnqueue(T item)
    {
        lock (_lockObj)
        {
            if (IsFull)
                return false;
                
            _buffer[_head] = item;
            _head = (_head + 1) % _capacity;
            _count++;
            return true;
        }
    }

    /// <summary>
    /// 尝试从缓冲区读取单个元素
    /// </summary>
    /// <param name="item">当方法返回时，包含读取到的元素；如果缓冲区为空，则为默认值</param>
    /// <returns>如果成功读取返回true，如果缓冲区为空返回false</returns>
    public bool TryDequeue(out T? item)
    {
        lock (_lockObj)
        {
            if (IsEmpty)
            {
                item = default;
                return false;
            }
                
            item = _buffer[_tail];
            _tail = (_tail + 1) % _capacity;
            _count--;
            return true;
        }
    }

    /// <summary>
    /// 向缓冲区添加多个元素
    /// </summary>
    /// <param name="items">要添加的元素数组</param>
    /// <param name="offset">起始偏移量</param>
    /// <param name="count">要添加的元素数量</param>
    /// <returns>实际添加的元素数量</returns>
    /// <exception cref="ArgumentNullException">当items为null时抛出</exception>
    /// <exception cref="ArgumentOutOfRangeException">当offset或count超出范围时抛出</exception>
    public int EnqueueRange(T[] items, int offset, int count)
    {
        ArgumentNullException.ThrowIfNull(items);
        
        if (offset < 0)
            throw new ArgumentOutOfRangeException(nameof(offset), "偏移量不能为负数");
        if (count < 0)
            throw new ArgumentOutOfRangeException(nameof(count), "数量不能为负数");
        if (offset + count > items.Length)
            throw new ArgumentException("偏移量和数量超出数组范围");

        lock (_lockObj)
        {
            int writeCount = 0;
            int availableSpace = _capacity - _count;
            int actualCount = Math.Min(count, availableSpace);
            
            for (int i = 0; i < actualCount; i++)
            {
                _buffer[_head] = items[offset + i];
                _head = (_head + 1) % _capacity;
                writeCount++;
            }
            
            _count += writeCount;
            return writeCount;
        }
    }

    /// <summary>
    /// 从缓冲区读取多个元素
    /// </summary>
    /// <param name="buffer">存储读取元素的数组</param>
    /// <param name="offset">起始偏移量</param>
    /// <param name="count">要读取的元素数量</param>
    /// <returns>实际读取的元素数量</returns>
    /// <exception cref="ArgumentNullException">当buffer为null时抛出</exception>
    /// <exception cref="ArgumentOutOfRangeException">当offset或count超出范围时抛出</exception>
    public int DequeueRange(T[] buffer, int offset, int count)
    {
        ArgumentNullException.ThrowIfNull(buffer);
        
        if (offset < 0)
            throw new ArgumentOutOfRangeException(nameof(offset), "偏移量不能为负数");
        if (count < 0)
            throw new ArgumentOutOfRangeException(nameof(count), "数量不能为负数");
        if (offset + count > buffer.Length)
            throw new ArgumentException("偏移量和数量超出数组范围");

        lock (_lockObj)
        {
            int readCount = 0;
            int actualCount = Math.Min(count, _count);
            
            for (int i = 0; i < actualCount; i++)
            {
                buffer[offset + i] = _buffer[_tail];
                _tail = (_tail + 1) % _capacity;
                readCount++;
            }
            
            _count -= readCount;
            return readCount;
        }
    }

    /// <summary>
    /// 清空缓冲区中的所有元素
    /// </summary>
    public void Clear()
    {
        lock (_lockObj)
        {
            _head = 0;
            _tail = 0;
            _count = 0;
            
            // 可选：清空数组中的引用，帮助GC回收
            if (typeof(T).IsClass)
            {
                Array.Clear(_buffer, 0, _capacity);
            }
        }
    }

    /// <summary>
    /// 查看缓冲区中的元素但不移除它们
    /// </summary>
    /// <param name="buffer">存储查看元素的数组</param>
    /// <param name="offset">起始偏移量</param>
    /// <param name="count">要查看的元素数量</param>
    /// <returns>实际查看的元素数量</returns>
    public int PeekRange(T[] buffer, int offset, int count)
    {
        ArgumentNullException.ThrowIfNull(buffer);
        
        if (offset < 0)
            throw new ArgumentOutOfRangeException(nameof(offset), "偏移量不能为负数");
        if (count < 0)
            throw new ArgumentOutOfRangeException(nameof(count), "数量不能为负数");
        if (offset + count > buffer.Length)
            throw new ArgumentException("偏移量和数量超出数组范围");

        lock (_lockObj)
        {
            int readCount = 0;
            int actualCount = Math.Min(count, _count);
            int currentTail = _tail;
            
            for (int i = 0; i < actualCount; i++)
            {
                buffer[offset + i] = _buffer[currentTail];
                currentTail = (currentTail + 1) % _capacity;
                readCount++;
            }
            
            return readCount;
        }
    }

    #endregion
}